#pragma once

#ifndef MATHS_H_INCLUDED
#define MATHS_H_INCLUDED


#include <algorithm>


using namespace Ogre;
using namespace std;



static void RotateMatrixRelativeY(Matrix4 &transform, float radians)
   {
   float    cosAngle;
   float    sinAngle;
   Matrix4  matrix1;

   memcpy(&matrix1, &transform, sizeof(Matrix4));

   cosAngle = cosf(radians);
   sinAngle = sinf(radians);

   transform[0][0] = (matrix1[0][0] *  cosAngle) + (matrix1[0][2] * sinAngle);
   transform[0][2] = (matrix1[0][0] * -sinAngle) + (matrix1[0][2] * cosAngle);
   transform[1][0] = (matrix1[1][0] *  cosAngle) + (matrix1[1][2] * sinAngle);
   transform[1][2] = (matrix1[1][0] * -sinAngle) + (matrix1[1][2] * cosAngle);
   transform[2][0] = (matrix1[2][0] *  cosAngle) + (matrix1[2][2] * sinAngle);
   transform[2][2] = (matrix1[2][0] * -sinAngle) + (matrix1[2][2] * cosAngle);
   }


static DWORD randDWORD(DWORD low, DWORD high)
   {
   //   if (high == low)
   //      return low;

   DWORD range = high - low + 1;
   DWORD num = ((DWORD)rand() * (DWORD)rand()) % range;

   return(num + low);
   }


static int randInt(int low, int high)
   {
   int   num;
   DWORD range;

   range = high - low + 1;
   if (range > RAND_MAX)
      num = (int)(((DWORD)rand() * (DWORD)rand()) % range);
   else
      num = (rand()) % range;

   return(num + low);
   }


static float randFloat(float low, float high)
   {
   int tempRand;

   tempRand = randInt((int)(low * 100.0f), (int)(high * 100.0f));
   return((float)tempRand / 100.0f);
   }



// If num_digits is 0, then number is rounded to the nearest integer.
// Examples
//        ROUND(2.15, 1)        equals 2.2
//        ROUND(2.149, 1)        equals 2.1
//        ROUND(-1.475, 2)    equals -1.48
// Source of Round() from: http://www.codeproject.com/cpp/floatutils.asp
static float Round(const float &number, const int num_digits)
   {
   float doComplete5i;
   float doComplete5 = number * powf(10.0f, (float)(num_digits + 1));

   if (number < 0.0f)
      doComplete5 -= 5.0f;
   else
      doComplete5 += 5.0f;

   doComplete5 /= 10.0f;
   modff(doComplete5, &doComplete5i);

   return doComplete5i / powf(10.0f, (float)num_digits);
   }



// Rounds X to SigFigs significant figures.
// Examples
//        SigFig(1.23456, 2)        equals 1.2
//        SigFig(1.23456e-10, 2)    equals 1.2e-10
//        SigFig(1.23456, 5)        equals 1.2346
//        SigFig(1.23456e-10, 5)    equals 1.2346e-10
//        SigFig(0.000123456, 2)    equals 0.00012
// Source of SigFig() from: http://www.codeproject.com/cpp/floatutils.asp
static float SigFig(float x, int sigFigs)
   {
   if (sigFigs < 1)
      {
      assert(FALSE);
      return x;
      }

   // log10f(0) returns NaN
   if (x == 0.0f)
      return x;

   int Sign;
   if (x < 0.0f)
      Sign = -1;
   else
      Sign = 1;

   x = fabsf(x);
   float Powers = powf(10.0f, floorf(log10f(x)) + 1.0f);
   return Sign * Round(x / Powers, sigFigs) * Powers;
   }



static BOOL LineSegmentsIntersect(Vector2 &point1a, Vector2 &point1b, Vector2 &point2a, Vector2 &point2b, Vector2 &intersection)
   {
   float denom;
   float uanumer;
   float ua;
   float x;
   float y;

   //   denom = (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
   //   uanumer = (x4 - x3)(y1 - y3) - (y4 - y3)(x1 - x3)
   //   ubnumer = (x2 - x1)(y1 - y3) - (y2 - y1)(x1 - x3)
   //   x = x1 + ua (x2 - x1)
   //   y = y1 + ua (y2 - y1)

   denom = ((point2b.y - point2a.y)) * (point1b.x - point1a.x) - ((point2b.x - point2a.x) * (point1b.y - point1a.y));
   if (denom == 0.0f)
      return FALSE;

   uanumer = ((point2b.x - point2a.x) * (point1a.y - point2a.y)) - ((point2b.y - point2a.y) * (point1a.x - point2a.x));

   ua = uanumer / denom;

   x = point1a.x + (ua * (point1b.x - point1a.x));
   y = point1a.y + (ua * (point1b.y - point1a.y));

   intersection.x = x;
   intersection.y = y;

   // Lessen floating-point errors.
   x = Round(x, 5);
   y = Round(y, 5);

   if (x < min(point1a.x, point1b.x))
      return FALSE;
   if (x > max(point1a.x, point1b.x))
      return FALSE;
   if (x < min(point2a.x, point2b.x))
      return FALSE;
   if (x > max(point2a.x, point2b.x))
      return FALSE;

   if (y < min(point1a.y, point1b.y))
      return FALSE;
   if (y > max(point1a.y, point1b.y))
      return FALSE;
   if (y < min(point2a.y, point2b.y))
      return FALSE;
   if (y > max(point2a.y, point2b.y))
      return FALSE;

   return TRUE;
   }


// This function assumes that the line between point1-point2 does at least intersect the box.
static void ClipLineToBox(Vector2 &point1, Vector2 &point2, Rect &theBox)
   {
   // Test the trivial case of both points being within the box, and return if true (no intersections).
   if ((point1.x >= theBox.left) && (point1.x <= theBox.right) && (point1.y >= theBox.top) && (point1.y <= theBox.bottom) &&
       (point2.x >= theBox.left) && (point2.x <= theBox.right) && (point2.y >= theBox.top) && (point2.y <= theBox.bottom))
       return;

   Vector2 boxLinePoint1;
   Vector2 boxLinePoint2;
   Vector2 intersection;

   boxLinePoint1.x = theBox.left;
   boxLinePoint1.y = theBox.top;
   boxLinePoint2.x = theBox.right;
   boxLinePoint2.y = theBox.top;
   if (LineSegmentsIntersect(point1, point2, boxLinePoint1, boxLinePoint2, intersection))
      {
      // Line intersects the top section of the box.
      // Clip the higher point (lower-valued) to the intersection.
      if (point1.y < point2.y)
         {
         point1.x = intersection.x;
         point1.y = intersection.y;
         }
      else
         {
         point2.x = intersection.x;
         point2.y = intersection.y;
         }
      }

   boxLinePoint1.x = theBox.right;
   boxLinePoint1.y = theBox.top;
   boxLinePoint2.x = theBox.right;
   boxLinePoint2.y = theBox.bottom;
   if (LineSegmentsIntersect(point1, point2, boxLinePoint1, boxLinePoint2, intersection))
      {
      // Line intersects the right section of the box.
      // Clip the further-right point to the intersection.
      if (point1.x > point2.x)
         {
         point1.x = intersection.x;
         point1.y = intersection.y;
         }
      else
         {
         point2.x = intersection.x;
         point2.y = intersection.y;
         }
      }

   boxLinePoint1.x = theBox.left;
   boxLinePoint1.y = theBox.bottom;
   boxLinePoint2.x = theBox.right;
   boxLinePoint2.y = theBox.bottom;
   if (LineSegmentsIntersect(point1, point2, boxLinePoint1, boxLinePoint2, intersection))
      {
      // Line intersects the bottom section of the box.
      // Clip the lower point (higher-valued) to the intersection.
      if (point1.y > point2.y)
         {
         point1.x = intersection.x;
         point1.y = intersection.y;
         }
      else
         {
         point2.x = intersection.x;
         point2.y = intersection.y;
         }
      }

   boxLinePoint1.x = theBox.left;
   boxLinePoint1.y = theBox.top;
   boxLinePoint2.x = theBox.left;
   boxLinePoint2.y = theBox.bottom;
   if (LineSegmentsIntersect(point1, point2, boxLinePoint1, boxLinePoint2, intersection))
      {
      // Line intersects the left section of the box.
      // Clip the further-left point to the intersection.
      if (point1.x < point2.x)
         {
         point1.x = intersection.x;
         point1.y = intersection.y;
         }
      else
         {
         point2.x = intersection.x;
         point2.y = intersection.y;
         }
      }
   }

#endif // MATHS_H_INCLUDED
